#region Copyright Vanderlande Industries B.V. 2009-2012
//
// Copyright (c) 2009-2012 Vanderlande Industries.
// All rights reserved.
//
// @file:     FscConfigurationFileReader.cs
// @version:  %version: 30 %
// %date_created: woensdag 6 maart 2013 18:24:38 %
//
// The copyright to the computer program(s) herein is the property of
// Vanderlande Industries. The program(s) may be used and/or copied
// only with the written permission of the owner or in accordance with
// the terms and conditions stipulated in the contract under which the
// program(s) have been supplied.
//
#endregion

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Xml;
//using VI.Bfw.Logging;
using VI.Fsc.Common.Util;
using VI.Fsc.OperatorGui.Models.Fsc.Configuration;
using VI.Fsc.OperatorGui.Models.Application.Util;

namespace VI.Fsc.OperatorGui.Models.Application
{
    /// <summary>
    /// The <b>CFscConfigurationFileReader</b> class reads the fsc configuration from the <i>VISION.exe.config</i>.
    /// </summary>
    /// <remarks>
    /// This class must be defined as the section handler for the <i>fscconfig</i> section in the VISION.exe.config.
    /// </remarks>
    public class CFscConfigurationFileReader : IConfigurationSectionHandler
    {
        private const string SUPPORTED_PROTOCOL = "http://";
        private const string PORT_SEPARATOR = ":";
        private const string PATH_SEPARATOR = "/";

        /// <summary>
        /// Creates the fsc configuration reader.
        /// </summary>
        public CFscConfigurationFileReader()
        {
        }

        /// <summary>
        /// Given a port and the extra part of the address, an address is returned.
        /// </summary>
        /// <param name="port">The port number to use. Can be null.</param>
        /// <param name="addressPath">The extra part of the address, to be added
        /// after the base ip and the port number. Can be null.</param>
        /// <returns></returns>
        private string CompleteIpAddress(string port, string addressPath)
        {
            string ip = "";

            if (!String.IsNullOrEmpty(port))
            {
                ip += PORT_SEPARATOR + port;
            }
            if (!String.IsNullOrEmpty(addressPath))
            {
                if (!addressPath.StartsWith(PATH_SEPARATOR))
                {
                    ip += PATH_SEPARATOR;
                }
                ip += addressPath;
                if (!addressPath.EndsWith(PATH_SEPARATOR))
                {
                    ip += PATH_SEPARATOR;
                }
            }
            return ip;
        }

        /// <summary>
        /// Creates a WebServiceSettings object, based on the attributes of the xml node.
        /// The url of the web service will use the given ip address as a base.
        /// </summary>
        /// <param name="node">The xml node that contains the xml element with the web service settings.</param>
        /// <param name="baseIp">The base ip to eb used to call the web service.</param>
        /// <param name="webServiceTagName">The name of the xml node that contains the info needed.</param>
        /// <returns></returns>
        private CFscWebServiceSettings GetWebServiceSettings(XmlElement node, string webServiceTagName)
        {
            CFscWebServiceSettings webServiceSettings = null;
            XmlNodeList list = node.GetElementsByTagName(webServiceTagName);

            if (list.Count > 0)
            {
                XmlElement element = (XmlElement)list[0];
                string port = element.GetAttribute("port");
                string path = element.GetAttribute("path");
                int pingRate = Convert.ToInt32(element.GetAttribute("pingrate"));

                webServiceSettings = 
                    new CFscWebServiceSettings(
                        CompleteIpAddress(port, path),
                        pingRate
                    );
            }

            return webServiceSettings;
        }

        /// <summary>
        /// Given an initial path for the commands, and a command name, a full command path is returned.
        /// </summary>
        /// <param name="commandInitialPath">The initial part of the command path.</param>
        /// <param name="commandName">The name of the command.</param>
        /// <returns></returns>
        protected string CompleteCommandPath(string commandInitialPath, string commandName)
        {
            string path = commandInitialPath.TrimEnd(PATH_SEPARATOR.ToCharArray()) + 
                PATH_SEPARATOR + commandName.TrimStart(PATH_SEPARATOR.ToCharArray());

            return path;
        }

        private string GetAttributeValueFromXml(XmlElement xmlNode, string commandTagName, string attributeName)
        {
            string attributeValue = "";
            XmlNodeList commandList = xmlNode.GetElementsByTagName(commandTagName);

            if (commandList.Count > 0)
            {
                attributeValue = ((XmlElement)commandList[0]).GetAttribute(attributeName);
            }
            //Logger.Debug(MethodBase.GetCurrentMethod(), "Attribute " + attributeName + " of tag " + commandTagName +
                //" in XML-node " + xmlNode.BaseURI + " has value " + attributeValue);
            return attributeValue;
        }

        /// <summary>
        /// Given an xml node containing the info of a command, and given the name of the
        /// command, retrieves the real name of the command.
        /// </summary>
        /// <param name="commandTagName">The command name (xml tag name).</param>
        /// <param name="xmlNode">The xml node containing the real command name.</param>
        /// <returns></returns>
        protected string GetCommandNameFromXml(string commandTagName, XmlElement xmlNode)
        {
            return GetAttributeValueFromXml(xmlNode, commandTagName, "name");
        }
        // Given an xml node containing the info of a command, and given the name of the command,
        // retrieves the information whether a button is shown for this command in the sidebar menu.
        protected bool GetShowButtonForCommand(string commandTagName, XmlElement xmlNode)
		{
            return Convert.ToBoolean(GetAttributeValueFromXml(xmlNode, commandTagName, "showButton"));
        }

        /// <summary>
        /// Given an xml node containing the info of a command, and given the name of the
        /// command, retrieves the real name of the command.
        /// </summary>
        /// <param name="commandTagName">The command name (xml tag name).</param>
        /// <param name="xmlNode">The xml node containing the real command name.</param>
        /// <returns></returns>
        private string GetMaintenanceModeFromXml(string commandTagName, XmlElement xmlNode)
        {
            string name = "";
            XmlNodeList commandList = xmlNode.GetElementsByTagName(commandTagName);

            if (commandList.Count > 0)
            {
                name = ((XmlElement)commandList[0]).GetAttribute("available");
            }

            return name;
        }

        /// <summary>
        /// Gives a dictionary containing the path of a command together with the information, whether a sidebar-button
        /// is to be displayed.
        /// </summary>
        /// <remarks>This method is called for the Operator GUI. For the Operator GUI, the sidebar buttons
        /// for start/stop/reset are displayed without considering the configuration.</remarks>
        /// <param name="xmlNode">The XML-node the commands are retrieved from.</param>
        /// <returns>Gives a dictionary containing the path of a command together with the information,
        /// whether a sidebar-button is to be displayed.</returns>
        protected virtual Dictionary<FscCommands, FscCommandInfo> GetListOfCommandsFromXml(XmlElement xmlNode)
        {
            Dictionary<FscCommands, FscCommandInfo> commandsHash = new Dictionary<FscCommands, FscCommandInfo>();
            XmlNodeList list = xmlNode.GetElementsByTagName("commands");

            if (list.Count > 0)
            {
                string commandInitialPath = ((XmlElement)list[0]).GetAttribute("initialPath");
                commandsHash.Add(FscCommands.COMMAND_NAME_START,
                    new FscCommandInfo(this.CompleteCommandPath(commandInitialPath,
                        this.GetCommandNameFromXml("start", (XmlElement)list[0])),
                    true));
                
                commandsHash.Add(FscCommands.COMMAND_NAME_STOP, 
                    new FscCommandInfo(this.CompleteCommandPath(commandInitialPath,
                        this.GetCommandNameFromXml("stop", (XmlElement)list[0])),
                    true));
                
                commandsHash.Add(FscCommands.COMMAND_NAME_RESET, 
                    new FscCommandInfo(this.CompleteCommandPath(commandInitialPath,
                        this.GetCommandNameFromXml("reset", (XmlElement)list[0])),
                    true));
                
                commandsHash.Add(
                    FscCommands.COMMAND_NAME_SETSPEED, 
                    new FscCommandInfo(this.CompleteCommandPath(commandInitialPath,
                        this.GetCommandNameFromXml("setspeed", (XmlElement)list[0])),
                    false));
            }

            return commandsHash;
        }

        /// <summary>
        /// For a given XML node create a FSC configuration object.
        /// </summary>
        /// <param name="fscNode">The configuration node of an FSC.</param>
        protected virtual void CreateFscFromXml(XmlElement fscNode)
        {
            CFscWebServiceSettings opcWebServiceSettings;
            CFscWebServiceSettings userEventsWebServiceSettings;
            Dictionary<FscCommands, FscCommandInfo> commands = new Dictionary<FscCommands, FscCommandInfo>();

            if (!string.IsNullOrEmpty(OperatorGuiApplicationSettings.Instance.FscIpAddress))
            {
                string areaDescription = fscNode.GetAttribute("displayName");

                // Get the settings of the web services.
                opcWebServiceSettings = GetWebServiceSettings(fscNode, "opcserver");
                userEventsWebServiceSettings = GetWebServiceSettings(fscNode, "userevents");

                // Get the commands.
                commands = GetListOfCommandsFromXml(fscNode);

                // Creates a new FSC configuration object.
                CFscConfiguration.Instance.Set(areaDescription, opcWebServiceSettings, userEventsWebServiceSettings,
                    commands);

                // Get the load from xml value.
                CFscConfiguration.Instance.LoadOpcStructureFromXml = 
                    GetBooleanAttributeValueFromXml(fscNode, "startup", "loadOpcStructureFromXml", false);
                    
                    //GetLoadOpcStructureFromXml(fscNode);
                CFscConfiguration.Instance.EnableDebugFile = 
                    GetBooleanAttributeValueFromXml(fscNode, "startup", "enableDebugFile", false);

                CFscConfiguration.Instance.EnableModeButton =
                    GetBooleanAttributeValueFromXml(fscNode, "startup", "enableModeButton", false);
            }
            else
            {
                string ErrorMessage = "IP Adress not available in registry !! Please use installer again and enter the correct IP address of the FSC";
                //Logger.Error(MethodBase.GetCurrentMethod(), ErrorMessage);
                throw new ConfigurationErrorsException(ErrorMessage);
            }
        }

        private bool GetBooleanAttributeValueFromXml(XmlElement fscNode, string tagName, string attributeName, bool defaultValue)
        {
            // Default, do not load from xml if the value is not available in the configuration file.
            bool returnValue = defaultValue;
            XmlNodeList elementList = fscNode.GetElementsByTagName(tagName);

            foreach (XmlElement element in elementList)
            {
                string loadFromXml = element.GetAttribute(attributeName);
                if (!string.IsNullOrEmpty(loadFromXml))
                {
                    returnValue = Convert.ToBoolean(loadFromXml);
                }
            }
            return returnValue;
        }

        /// <summary>
        /// Handles the access to <i>fscconfig</i> configuration sections.
        /// </summary>
        /// <returns>Always <i>null</i>.</returns>
        public virtual object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());
            
            // 1. Get all the FSCs tags.
            XmlNodeList fscsList = ((XmlElement)section).GetElementsByTagName("fsc");

            // 2. For each node, parse and create an fsc item.
            foreach (XmlElement fscNode in fscsList)
            {
                CreateFscFromXml(fscNode);
            }

            return new object();
        }
    }
}
